package com.house.framework.utils;

import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.lang.management.ManagementFactory;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 时间工具类
 *
 * @author ruoyi
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
    public static String YYYY = "yyyy";

    public static String YYYY_MM = "yyyy-MM";

    public static String YYYY_MM_DD = "yyyy-MM-dd";

    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";

    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    public static String YYYY_MM_DD_HH = "yyyy-MM-dd HH";
    public static String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
    public static String YYYY_MM_DD_HH_00 = "yyyy-MM-dd HH:00";
    public final static String HH_MM = "HH:mm";
    public final static String HH_00 = "HH:00";

    public static String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS";

    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};

    /**
     * 获取当前Date型日期
     *
     * @return Date() 当前日期
     */
    public static Date getNowDate() {
        return new Date();
    }

    /**
     * 获取当前日期, 默认格式为yyyy-MM-dd
     *
     * @return String
     */
    public static String getDate() {
        return dateTimeNow(YYYY_MM_DD);
    }

    public static final String getTime() {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
    }

    public static final String getTimeToHours() {
        return dateTimeNow(YYYY_MM_DD_HH);
    }

    public static final String dateTimeNow() {
        return dateTimeNow(YYYYMMDDHHMMSS);
    }

    public static final String dateTimeNow(final String format) {
        return parseDateToStr(format, new Date());
    }

    public static final String dateTime(final Date date) {
        return parseDateToStr(YYYY_MM_DD, date);
    }

    public static final String parseDateToStr(final String format, final Date date) {
        try {
            return new SimpleDateFormat(format).format(date);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    public static String getFormatNowDateTimeFF() {
        SimpleDateFormat dateFromat = new SimpleDateFormat("yyyyMMddHHmmss");
        return dateFromat.format(new Date()) + (int) ((Math.random() * 9.0D + 1.0D) * 10.0D);
    }

    public static Date parseDate(String str, String... parsePatterns) {
        Date date;
        try {
            date = parseDate(str, (Locale) null, parsePatterns);
        } catch (ParseException e) {
            e.printStackTrace();
            date = null;
        }
        return date;
    }

    public static final Date dateTime(final String format, final String ts) {
        try {
            return new SimpleDateFormat(format).parse(ts);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 日期路径 即年/月/日 如2018/08/08
     */
    public static final String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }

    /**
     * 日期路径 即年/月/日 如20180808
     */
    public static final String dateTime() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyyMMdd");
    }

    /**
     * 日期型字符串转化为日期 格式
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        return parseDate(str.toString(), parsePatterns);
    }

    /**
     * 获取服务器启动时间
     */
    public static Date getServerStartDate() {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }

    /**
     * 计算两个时间差
     */
    public static String getDatePoor(Date endDate, Date nowDate) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - nowDate.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }

    /**
     * 计算两个时间差
     *
     * @param startStr
     * @param endStr
     * @return
     */
    public static int getDateDiffDayForStr(String startStr, String endStr) {
        LocalDate localDateStart = LocalDate.parse(startStr, DateTimeFormatter.ISO_DATE);
        LocalDate localDateEnd = LocalDate.parse(endStr, DateTimeFormatter.ISO_DATE);
        Period next = Period.between(localDateStart, localDateEnd);
        return next.getDays();
    }

    /**
     * 计算两个时间差
     *
     * @param startStr
     * @param endStr
     * @return
     */
    public static Period getDateDiff(String startStr, String endStr) {
        LocalDate localDateStart = LocalDate.parse(startStr, DateTimeFormatter.ISO_DATE);
        LocalDate localDateEnd = LocalDate.parse(endStr, DateTimeFormatter.ISO_DATE);
        Period next = Period.between(localDateStart, localDateEnd);
        return next;
    }

    public static String getDateLineSql(int dateType, String startDate, String endDate, String dateFieldAsName) {
        return getDateLineSql(dateType, startDate, endDate, dateFieldAsName, "");
    }

    /**
     * 1小时 11自动给结束时间➕一天 2日3月4年
     *
     * @param dateType
     * @param dateFieldAsName 时间别名
     * @return
     */
    public static String getDateLineSql(int dateType, String startDate, String endDate, String dateFieldAsName, String ft) {
        String as = "F_TIME";
        if (StringUtils.isNotEmpty(dateFieldAsName)) {
            as = dateFieldAsName;
        }
        String format = ft;
        String sql = "";
        switch (dateType) {
            case 1:
                sql = " SELECT\n" +
                        "\tto_char(to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'yyyy-mm-dd hh24') " + as + ", \n" +

                        "\tto_char(to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'yyyy') F_YEAR, \n" +
                        "\tto_char(to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'mm') F_MONTH, \n" +
                        "\tto_char(to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'dd') F_DAY, \n" +
                        "\tto_char(to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'hh24') F_HOUR \n" +
                        " FROM\n" +
                        "\tdual CONNECT BY ROWNUM <= ( to_date ( '" + endDate + "', 'yyyy-mm-dd hh24' ) - to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) ) * 24 +1 ";
                break;
            case 11:
                if (StringUtils.isEmpty(format)) {
                    format = "yyyy-mm-dd hh24";
                }
                sql = " SELECT\n" +
                        "\tto_char(to_date ( '" + startDate + "', '" + format + "' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'yyyy-mm-dd hh24') AS " + as + ", \n" +
                        "\tto_char(to_date ( '" + startDate + "', '" + format + "' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'yyyy') F_YEAR, \n" +
                        "\tto_char(to_date ( '" + startDate + "', '" + format + "' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'mm') F_MONTH, \n" +
                        "\tto_char(to_date ( '" + startDate + "', '" + format + "' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'dd') F_DAY, \n" +
                        "\tto_char(to_date ( '" + startDate + "', '" + format + "' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'hh24') F_HOUR \n" +
                        " FROM\n" +
                        "\tdual CONNECT BY ROWNUM <= ( (to_date ( '" + endDate + "', '" + format + "' )+1) - to_date ( '" + startDate + "', '" + format + "' ) ) * 24  ";
                break;
            case 2:
                sql = " SELECT\n" +
                        "\tto_char((to_date ( '" + startDate + "', 'yyyy-mm-dd' ) + ROWNUM - 1),'yyyy-mm-dd') " + as + " ,\n" +
                        "\tto_char((to_date ( '" + startDate + "', 'yyyy-mm-dd' ) + ROWNUM - 1),'mm') as F_MONTH, \n" +
                        "\tto_char((to_date ( '" + startDate + "', 'yyyy-mm-dd' ) + ROWNUM - 1),'dd') as F_DAY \n" +
                        " FROM\n" +
                        "\tdual CONNECT BY ROWNUM <= to_date ( '" + endDate + "', 'yyyy-mm-dd' ) - to_date ( '" + startDate + "', 'yyyy-mm-dd' ) + 1 ";
                break;
            case 3:
                sql = " SELECT\n" +
                        "\tto_char ( add_months ( to_date ( '" + startDate + "', 'yyyy-mm' ), ROWNUM - 1 ), 'yyyy-mm' ) " + as + ", \n" +
                        "\tto_char ( add_months ( to_date ( '" + startDate + "', 'yyyy-mm' ), ROWNUM - 1 ), 'mm' ) F_MONTH \n" +
                        "FROM\n" +
                        "\tdual CONNECT BY ROWNUM <= months_between ( to_date ( '" + endDate + "', 'yyyy-mm' ), to_date ( '" + startDate + "', 'yyyy-mm' ) ) + 1 ";
                break;
            case 4:
                sql = "SELECT\n" +
                        "\tTO_CHAR ( ADD_MONTHS ( TO_DATE ( '" + startDate + "', 'YYYY-MM-DD' ), ( ROWNUM - 1 ) * 12 ), 'YYYY' ) AS " + as + ", \n" +
                        "\tTO_CHAR ( ADD_MONTHS ( TO_DATE ( '" + startDate + "', 'YYYY-MM-DD' ), ( ROWNUM - 1 ) * 12 ), 'YYYY' ) AS F_YEAR \n" +
                        "FROM\n" +
                        "\tDUAL CONNECT BY ROWNUM <= CEIL ( MONTHS_BETWEEN ( TO_DATE ( '" + endDate + "', 'YYYY-MM-DD' ), TO_DATE ( '" + startDate + "', 'YYYY-MM-DD' ) ) / 12 )";
                break;
            default:
                sql = " SELECT\n" +
                        "\tto_char(to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'yyyy-mm-dd hh24') " + as + ", \n" +
                        "\tto_char(to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'mm') F_MONTH, \n" +
                        "\tto_char(to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'dd') F_DAY, \n" +
                        "\tto_char(to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) + numtodsinterval ( ROWNUM - 1, 'hour' ),'hh24') F_HOUR \n" +
                        " FROM\n" +
                        "\tdual CONNECT BY ROWNUM <= ( to_date ( '" + endDate + "', 'yyyy-mm-dd hh24' ) - to_date ( '" + startDate + "', 'yyyy-mm-dd hh24' ) ) * 24 +1 ";
                break;
        }

        return sql;

    }

    public static String plusDay(int num, String newDate) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Date currdate = null;

        try {
            format.parse(newDate);
            Calendar ca = Calendar.getInstance();
            ca.add(5, num);
            currdate = ca.getTime();
            String enddate = format.format(currdate);
            return enddate;
        } catch (ParseException var6) {
            return "";
        }
    }

    public static String changeTime(int field, int amount) {
        SimpleDateFormat format = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
        Calendar ca = Calendar.getInstance();
        ca.setTime(new Date());
        ca.add(Calendar.HOUR, amount);
        return format.format(ca.getTime());
    }

    public static String changeTimeToHour(int field, int amount) {
        SimpleDateFormat format = new SimpleDateFormat(YYYY_MM_DD_HH);
        Calendar ca = Calendar.getInstance();
        ca.setTime(new Date());
        ca.add(field, amount);
        return format.format(ca.getTime());
    }

    public static String getNowDateTimeStr() {
        SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return dateFromat.format(new Date());
    }

    public static String getNowDateTimeStrForExport() {
        SimpleDateFormat dateFromat = new SimpleDateFormat("yyyyMMddHHmmss");
        return dateFromat.format(new Date());
    }

    /**
     * 获取年份的每个季度开始结束日期
     */
    public static List<StartEndTime> getJdList(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_YEAR, 1);
        List<StartEndTime> jds = new ArrayList<>(4);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 1; i <= 4; i++) {
            StartEndTime startEndTime = new StartEndTime();

            startEndTime.setStartTime(simpleDateFormat.format(calendar.getTime()) + " 00:00:00");
            calendar.add(Calendar.MONTH, 3);
            calendar.add(Calendar.DAY_OF_YEAR, -1);

            startEndTime.setEndTime(simpleDateFormat.format(calendar.getTime()) + " 23:59:59");
            calendar.set(Calendar.DAY_OF_MONTH, 1);
            calendar.add(Calendar.MONTH, 1);

            jds.add(startEndTime);
        }
        return jds;
    }

    /**
     * @Description: 获取季度第一天
     * 1.根据给定日期计算当前季度的第一个月份
     * 2.设置日历的月份为当前季度的第一个月份
     * 3.最后设置日历月份天数为第一天即可
     **/
    public static Calendar getQuarterStart(Date date) {
        Calendar startCalendar = Calendar.getInstance();
        startCalendar.setTime(date);
        //get方法：获取给定日历属性的值，如 endCalendar.get(Calendar.MONTH) 获取日历的月份
        //计算季度数：由于月份从0开始，即1月份的Calendar.MONTH值为0,所以计算季度的第一个月份只需 月份 / 3 * 3
        startCalendar.set(Calendar.MONTH, (((int) startCalendar.get(Calendar.MONTH)) / 3) * 3);
        startCalendar.set(Calendar.DAY_OF_MONTH, 1);
        startCalendar.set(Calendar.HOUR_OF_DAY, 0);
        startCalendar.set(Calendar.MINUTE, 0);
        startCalendar.set(Calendar.SECOND, 0);
        startCalendar.set(Calendar.MILLISECOND, 0);
        return startCalendar;
    }

    /**
     * @Description: 获取季度最后一天
     **/
    public static Calendar getQuarterEnd(Date date) { // 季度结束
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(date);
        //计算季度数：由于月份从0开始，即1月份的Calendar.MONTH值为0,所以计算季度的第三个月份只需 月份 / 3 * 3 + 2
        endCalendar.set(Calendar.MONTH, ((endCalendar.get(Calendar.MONTH)) / 3) * 3 + 2);
        endCalendar.set(Calendar.DAY_OF_MONTH, endCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        endCalendar.set(Calendar.HOUR_OF_DAY, 23);
        endCalendar.set(Calendar.MINUTE, 59);
        endCalendar.set(Calendar.SECOND, 59);
        endCalendar.set(Calendar.MILLISECOND, 999);
        return endCalendar;
    }

    public static void main(String[] args) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(simpleDateFormat.format(getQuarterStart(new Date()).getTime()));
    }

    public static String getCurrentYearStartTime() {
        int year = Calendar.getInstance().get(Calendar.YEAR);
        return String.format("%s-01-01 00:00:00", year);
    }

    public static String getCurrentYearEndTime() {
        int year = Calendar.getInstance().get(Calendar.YEAR);
        return String.format("%s-12-31 23:59:59", year);
    }

    public static String getCurrentMonthStartTime() {
        Calendar calendar = Calendar.getInstance();
        return new SimpleDateFormat("yyyy-MM-01 00:00:00").format(calendar.getTime());
    }

    public static String getCurrentMonthEndTime() {
        Calendar calendar = Calendar.getInstance();
        int day = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        calendar.set(Calendar.DAY_OF_MONTH, day);
        return new SimpleDateFormat("yyyy-MM-dd 23:59:59").format(calendar.getTime());
    }


    public static String getMonthStartTime(Calendar calendar) {
        return new SimpleDateFormat("yyyy-MM-01 00:00:00").format(calendar.getTime());
    }

    public static String getMonthEndTime(Calendar calendar) {
        int day = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        calendar.set(Calendar.DAY_OF_MONTH, day);
        return new SimpleDateFormat("yyyy-MM-dd 23:59:59").format(calendar.getTime());
    }


    public static Date getCurrentDayStartDate() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    public static Date getCurrentDayEndDate() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        return cal.getTime();
    }

    public static String getLastMonthStartTime() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, -1);
        return new SimpleDateFormat("yyyy-MM-01 00:00:00").format(calendar.getTime());
    }

    public static String getLastMonthEndTime() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, -1);
        int day = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        calendar.set(Calendar.DAY_OF_MONTH, day);
        return new SimpleDateFormat("yyyy-MM-dd 23:59:59").format(calendar.getTime());
    }

    /**
     * date2比date1多的天数
     *
     * @param date1
     * @param date2
     * @return
     */
    public static int differentDays(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        int day1 = cal1.get(Calendar.DAY_OF_YEAR);
        int day2 = cal2.get(Calendar.DAY_OF_YEAR);

        int year1 = cal1.get(Calendar.YEAR);
        int year2 = cal2.get(Calendar.YEAR);
        if (year1 != year2) {//同一年
            int timeDistance = 0;
            for (int i = year1; i < year2; i++) {
                if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0)    //闰年
                {
                    timeDistance += 366;
                } else    //不是闰年
                {
                    timeDistance += 365;
                }
            }

            return timeDistance + (day2 - day1);
        } else {// 不同年
            return day2 - day1;
        }
    }

    /**
     * 计算两个日期相差小时数
     *
     * @param beginTime
     * @return
     */
    public static double getDiffHour(Date beginTime, Date endTime) {
        return new BigDecimal(endTime.getTime() - beginTime.getTime())
                .divide(new BigDecimal(1000 * 60 * 60), 1, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * @Description 获取两个时间段之间的小时几个数据
     * @Date 15:24 2023/3/17
     * @Param [startTime, endTime]
     * @return java.util.List<java.lang.String>
     **/
    @SneakyThrows
    public static List<String> getDateToHours(String startTime, String endTime){
        //定义时间格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(YYYY_MM_DD_HH);
        List<String> list = new ArrayList<>();
        // 转化成日期类型
        Date startDate = simpleDateFormat.parse(startTime);
        Date endDate = simpleDateFormat.parse(endTime);

        //用Calendar 进行日期比较判断
        Calendar calendar = Calendar.getInstance();
        while (startDate.getTime()<=endDate.getTime()){
            // 把日期添加到集合
            list.add(simpleDateFormat.format(startDate));
            // 设置日期
            calendar.setTime(startDate);
            //把日期增加一分
            calendar.add(Calendar.HOUR, 1);
            // 获取增加后的日期
            startDate=calendar.getTime();
        }
        return list;
    }
}
